import { ConfigureService } from '@app/configure';
import { DbService } from '@app/db';
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Post, Put, Query, Req } from '@nestjs/common';
import { BaseController } from '../../base/base.controller';
import { MemberService } from './member.service';
import { Role } from '@app/db/common/entities/Role';
import { FindOptionsWhere, In, Not } from 'typeorm';
import { AddDto, ChangePwdDto, EditDto, PageDto, UpdateDto } from './dto';
import { Member } from '@app/db/common/entities/Member';
import { MemberResult } from '../../types/result.type';
import { DtoPipe } from '../../pipes/dto/dto.pipe';
import { AuthService } from '../auth/auth.service';
import { createHash } from 'node:crypto'
import { CacheService } from '@app/cache';

@Controller('member')
export class MemberController extends BaseController{
    constructor(
        private readonly db:DbService,
        private readonly service:MemberService,
        private readonly auth:AuthService,
        private readonly config:ConfigureService,
        private readonly cache:CacheService
    ){
        super()
    }


    @Get('page')
    async rolePage(@Query() dto:PageDto, @Req() { userId, member }) {
        let result:PaginationResult<MemberResult> = {
            total : 0,
            rows : []
        }

        let where:FindOptionsWhere<Member> = {
            id : Not(userId)
        }

        if(member.belong == 0) {
            where.belong = 0
            where.super = false
        }else {
            where.tenantId = member.tenantId
        }

       

        result.total = await this.db.member.count(where)

        if(result.total > 0) {
            result.rows = await this.db.member.pagination(dto.pageNo, dto.pageSize, where, null, {id : 'DESC'})

            if(result.rows.length > 0) {
                
                const roleIds:number[] = []
                result.rows.forEach(row => {
                   
                    const roles = row.roles
                    if(roles) {
                        roles.forEach(role => {
                            if(roleIds.includes(role) == false) {
                                roleIds.push(role)
                            }
                        })
                    }
                })

               
                let roles:Role[] = []

                if(roleIds.length > 0) {
                    roles = await this.db.role.find({id : In(roleIds)}, ['name', 'id'])
                }


                if(roles.length > 0) {
                    result.rows.forEach(row => {
                        
                        const userRoles = row.roles as number[]
                        if(userRoles && userRoles.length > 0) {
                            row.roleNames = []
                            for(let i = 0; i < roles.length; i ++) {
                                if(userRoles.includes(roles[i].id)) {
                                    row.roleNames.push(roles[i].name)
                                }
                            }
                        }

                        
                    })
                }
            }

        }

        return this.successRes(result)

    }

    @Post('add')
    async add(@Body(new DtoPipe()) dto:AddDto, @Req() { userId, locale }) {
        const creator = await this.db.member.findOne({id : userId}, ['leaders', 'depts', 'directDept', 'tenantId', 'belong'])
        if(true == await this.db.member.isExistsByWhere({tenantId : creator.belong == 0 ? 0 : creator.tenantId, account : dto.account})) {
            return this.failRes(this.i18n.t(locale, 'accountExist'))
        }

        const lockKey = createHash('sha256').update(dto.account).digest('hex') + ':' + creator.tenantId
        const lock = await this.cache.set(lockKey, '1', 10, 'NX')
        
        if(!lock) {
            return this.failRes(this.i18n.t(locale, 'accountExist'))
        }

        const member:KeyToValueType<Member> = {
            creatorId : userId,
            belong : creator.belong,
            tenantId : creator.tenantId,
            directLeader : userId,
            registerTime : this.utils.time(),
            ...dto
        }

        member.leaders = creator.leaders || []
        member.leaders.push(userId)

        if(!dto.deptId && creator.directDept) { //没有选择部门，则默认和创建者同个部门
            member.directDept = creator.directDept
        }

        if(creator.depts) {
            member.depts = creator.depts
        }


        member.salt = this.auth.generateSalt()
        member.password = this.auth.buildPassword(member.password || this.config.get('adminDefaultLoginPwd'), member.salt)

    
        const res = await this.db.member.insert(member)

        this.cache.del(lockKey)

        return res ? this.successRes(res.affectedRows) : this.failRes()
    }

    @Put('edit/:id')
    async edit(@Body(new DtoPipe({skipMissingProperties : true})) dto:EditDto, @Param('id', ParseIntPipe) id:number, @Req() { belong, locale }) {
        if(id <= 0) return
        let password = ''
        if(dto.password) {
            password = dto.password
            delete dto.password
        }

        const member:KeyToValueType<Member> = {
            ...dto
        }

        let fields = Object.keys(member) as (keyof Member)[]
        if(fields.length == 0 && password === '') return

        const row = await this.db.member.findOne({ id }, fields)
        
        if(!row) return

        let updateData:KeyToValueType<Member> = {}


        for(let k in row) {
            if(row[k] != member[k]) {
                updateData[k] = member[k]
            }
        }

        if(password !== '') {
            updateData.password = password
        }

        if(Object.keys(updateData).length == 0) return this.successRes()

        
        if(updateData.password) {
            updateData.salt = this.auth.generateSalt()
            updateData.password = this.auth.buildPassword(updateData.password, updateData.salt)
        }

        const res = await this.db.member.update({id : id}, updateData)
        return res === 0 ? this.failRes() : this.successRes()
    }


    @Delete('delete/:id')
    async delete(@Param('id', ParseIntPipe) id:number) {
        const res = await this.db.member.delete({id : id})
        return res === 0 ? this.failRes() : this.successRes()
    }


    @Put('resetpwd')
    async resetpwd(@Body('id', ParseIntPipe) id:number) {
        const member = await this.db.member.findOne({id : id}, ['id'])
        if(!member) return
        const salt = this.auth.generateSalt()
        const password = this.auth.buildPassword(this.config.get('adminDefaultLoginPwd'), salt)

        this.db.member.update({id : id}, {salt, password})
       
        return this.successRes()
    }


    @Put('update')
    update(@Body(new DtoPipe({skipMissingProperties : true})) dto:UpdateDto, @Req() { userId }) {
        if(Object.keys(dto).length > 0) {
            this.db.member.update({ id : userId}, dto)
        }
        
        return this.successRes()
    }

    @Put('changePwd')
    async changePwd(@Body(new DtoPipe()) dto:ChangePwdDto, @Req() { userId, locale }) {
        const member = await this.db.member.findOne({id : userId}, ['password', 'salt'])
        if(this.auth.buildPassword(dto.oldpassword, member.salt) != member.password) return this.failRes(this.i18n.t(locale, 'passwordIncorrect'))

        const salt = this.auth.generateSalt()

        this.db.member.update({ id : userId}, {salt, password : this.auth.buildPassword(dto.password, salt)})
        return this.successRes()
    }
}
